package com.study.blogsearchproducer.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.study.blogsearchpojo.entity.Blog;
import com.study.blogsearchproducer.service.SearchService;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;

@Service
public class SearchServiceImpl implements SearchService {
    Logger LOGGER = LoggerFactory.getLogger(SearchServiceImpl.class);
    @Autowired
    RestHighLevelClient rhlClient;
    @Override
    public boolean exists(String indexName) {
        GetIndexRequest request = new GetIndexRequest();
        request.indices(indexName);
        // request.types(new String[]{indextype});
        boolean exists = false;
        try {
            exists = rhlClient.indices().exists(request, RequestOptions.DEFAULT);
            System.out.println(indexName+"exists =" + exists);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return exists;
    }

    @Override
    public boolean dropIndex(String indexName) {
        DeleteIndexRequest request = new DeleteIndexRequest(indexName);
        boolean acknowledged=false;
        try {
            AcknowledgedResponse delete = rhlClient.indices().delete(request, RequestOptions.DEFAULT);
            if(Objects.nonNull(delete)){
                System.out.println(indexName+"acknowledgeResponse ="+ delete.isAcknowledged());
                acknowledged = delete.isAcknowledged();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return acknowledged;
    }

    @Override
    public boolean createBlogIndex(String indexName) {
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        try {
            XContentBuilder mappingsBuilder = XContentFactory.jsonBuilder();
//            mappingsBuilder.startObject();
//            {
//                mappingsBuilder.startObject("properties");
//                {
//                    //博客id
//                    mappingsBuilder.startObject("blogId");
//                    {
//                        mappingsBuilder.field("type", "integer");
//                    }
//                    mappingsBuilder.endObject();
//                    //博客标题
//                    mappingsBuilder.startObject("title");
//                    {
//                        mappingsBuilder.field("type", "completion");
//                        mappingsBuilder.field("analyzer", "ik_max_word");
//                    }
//                    mappingsBuilder.endObject();
//                    //博客内容
//                    mappingsBuilder.startObject("content");
//                    {
//                        mappingsBuilder.field("type", "text");
//                        mappingsBuilder.field("analyzer", "ik_max_word");
//                    }
//                    mappingsBuilder.endObject();
//                    //博客作者
//                    mappingsBuilder.startObject("author");
//                    {
//                        mappingsBuilder.field("type", "keyword");
//                    }
//                    mappingsBuilder.endObject();
//                }
//                mappingsBuilder.endObject();
//            }
            mappingsBuilder.startObject();
            {
                mappingsBuilder.startObject("properties");
                {
                    //id
                    mappingsBuilder.startObject("purchaseId");
                    {
                        mappingsBuilder.field("type", "integer");
                    }
                    mappingsBuilder.endObject();
                    //一级分类id
                    mappingsBuilder.startObject("purchaseOneCategoryId");
                    {
                        mappingsBuilder.field("type", "integer");
                    }
                    mappingsBuilder.endObject();
                    //标题
                    mappingsBuilder.startObject("purchaseTitle");
                    {
                        mappingsBuilder.field("type", "text");
                        mappingsBuilder.field("analyzer", "standard");
                    }
                    mappingsBuilder.endObject();
                    //平台
                    mappingsBuilder.startObject("platformId");
                    {
                        mappingsBuilder.field("type", "integer");
                    }
                    mappingsBuilder.endObject();
                    //采购指南图片地址
                    mappingsBuilder.startObject("purchasePictureUrl");
                    {
                        mappingsBuilder.field("type", "text");
                    }
                    mappingsBuilder.endObject();
                    //排序值
                    mappingsBuilder.startObject("purchaseSort");
                    {
                        mappingsBuilder.field("type", "integer");
                    }
                    mappingsBuilder.endObject();
                    //状态
                    mappingsBuilder.startObject("purchaseStatus");
                    {
                        mappingsBuilder.field("type", "integer");
                    }
                    mappingsBuilder.endObject();
                    //是否删除
                    mappingsBuilder.startObject("isDel");
                    {
                        mappingsBuilder.field("type", "integer");
                    }
                    mappingsBuilder.endObject();
                    //SEO标题
                    mappingsBuilder.startObject("purchaseSeoTitle");
                    {
                        mappingsBuilder.field("type", "text");
                        mappingsBuilder.field("analyzer", "ik_max_word");
                    }
                    mappingsBuilder.endObject();
                    //SEO关键字
                    mappingsBuilder.startObject("purchaseSeoKeywords");
                    {
                        mappingsBuilder.field("type", "text");
                        mappingsBuilder.field("analyzer", "ik_max_word");
                    }
                    mappingsBuilder.endObject();
                    //SEO描述
                    mappingsBuilder.startObject("purchaseSeoContent");
                    {
                        mappingsBuilder.field("type", "text");
                        mappingsBuilder.field("analyzer", "ik_max_word");
                    }
                    mappingsBuilder.endObject();
                    //SEO概要
                    mappingsBuilder.startObject("purchaseSeoTips");
                    {
                        mappingsBuilder.field("type", "text");
                        mappingsBuilder.field("analyzer", "ik_max_word");
                    }
                    mappingsBuilder.endObject();
                    //nested
                    mappingsBuilder.startObject("purchaseFloorNested");
                    {
                        mappingsBuilder.field("type", "nested");
                        mappingsBuilder.startObject("properties");
                        {
                            mappingsBuilder.startObject("floorTitle");
                            {
                                mappingsBuilder.field("type", "text");
                                mappingsBuilder.field("analyzer", "standard");
                            }
                            mappingsBuilder.endObject();
                            mappingsBuilder.startObject("floorSort");
                            {
                                mappingsBuilder.field("type", "integer");
                            }
                            mappingsBuilder.endObject();
                        }
                        mappingsBuilder.endObject();
                    }
                    mappingsBuilder.endObject();
                }
                mappingsBuilder.endObject();
            }
            mappingsBuilder.endObject();
            request.mapping(mappingsBuilder);
        } catch (IOException e) {
            e.printStackTrace();
        }
        boolean acknowledged = false;
        try {
            CreateIndexResponse createIndexResponse = rhlClient.indices().create(request, RequestOptions.DEFAULT);
            System.out.println("createIndexResponse.isAcknowledged() = " + createIndexResponse.isAcknowledged());
            acknowledged = createIndexResponse.isAcknowledged();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return acknowledged;
    }

    @Override
    public IndexResponse saveOne(String indexName, Blog blog) {
        //设置id ，那么如果没有这个id的话新增一条，否则覆盖之前的id的其他字段
        IndexRequest indexRequest = new IndexRequest(indexName).id(blog.getBlogId().toString()).source(JSON.toJSONString(blog), XContentType.JSON);
        IndexResponse index = null;
        try {
            index = rhlClient.index(indexRequest, RequestOptions.DEFAULT);
            System.out.println("录入一条博客 响应：" + JSON.toJSONString(index));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return index;
    }

    @Override
    public BulkResponse addBlogsInBatches(String indexName, List<Blog> blogList) {
        // 创建BulkRequest对象
        BulkRequest bulkRequest = new BulkRequest("");
        // 创建多个bulkRequest对象
        for (Blog blog : blogList) {
            bulkRequest.add(new IndexRequest(indexName).id(blog.getBlogId().toString()).source(JSONObject.toJSONString(blog), XContentType.JSON));
        }
        // 请求并响应
        BulkResponse bulkresponse = null;
        try {
            bulkresponse = rhlClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            System.out.println("批量录入博客 bulkresponse.status() = " + bulkresponse.status());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bulkresponse;
    }

    @Override
    public boolean existsOne(String indexName, String id) {
        GetRequest getRequest = new GetRequest(indexName).id(id);
        boolean exists = false;
        try {
            GetResponse getResponse = rhlClient.get(getRequest, RequestOptions.DEFAULT);
            LOGGER.info("exists:{}",getResponse.isExists());
            exists = getResponse.isExists();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return exists;
    }

    @Override
    public DeleteResponse deleteOne(String indexName, String id) {
        DeleteRequest deleteRequest = new DeleteRequest(indexName).id(id);
        DeleteResponse deleteResponse = null;
        try {
            deleteResponse =rhlClient.delete(deleteRequest, RequestOptions.DEFAULT);
            if (deleteResponse.getResult() == DocWriteResponse.Result.DELETED) {
                LOGGER.info("INDEX DELETE SUCCESS");
            } else {
                LOGGER.info("INDEX DELETE FAILED");
            }
        } catch (IOException e) {
            e.printStackTrace();
            LOGGER.info("INDEX DELETE FAILED");
        }
        return deleteResponse;
    }

    @Override
    public UpdateResponse updateOne(String indexName, Blog blog) {
        UpdateRequest updateRequest;
        UpdateResponse updateResponse = null;
        try {
            updateRequest = new UpdateRequest().index(indexName).id(blog.getBlogId().toString());
            XContentBuilder builder = XContentFactory.jsonBuilder();
            builder.startObject();
            {
                builder.field("title", blog.getBlogTitle());
                builder.field("author", blog.getBlogAuthor());
                builder.field("content", blog.getBlogContent());
            }
            builder.endObject();
            updateRequest.doc(builder);
            updateResponse = rhlClient.update(updateRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (Objects.nonNull(updateResponse)&& updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
            System.out.println("INDEX CREATE SUCCESS");
        } else {
            if (updateResponse.getResult() != DocWriteResponse.Result.UPDATED && updateResponse.getResult() != DocWriteResponse.Result.NOOP) {
                System.out.println("INDEX UPDATE FAIL");
            }else {
                System.out.println("INDEX UPDATE SUCCESS");
            }
        }
        return updateResponse;
    }

    @Override
    public List<Blog> search(String indexName,String keyword,int pageNo) {
        List<Blog> blogList = new ArrayList<>();
        HighlightBuilder.Field contentField = new HighlightBuilder.Field("blogContent")
                .preTags("<span style='color:red'><em>")
                .postTags("</em></span>")
                .requireFieldMatch(false);
        HighlightBuilder.Field titleField = new HighlightBuilder.Field("blogTitle")
                .preTags("<span style='color:red'><em>")
                .postTags("</em></span>")
                .requireFieldMatch(false);
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field(contentField).field(titleField);

        SearchRequest searchRequest = new SearchRequest(indexName);

        // 条件构建工具:查询、分页、高亮、聚合、排序、字段过滤
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.highlighter(highlightBuilder);

        // 在条件工具中添加查询条件,分词查询
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
////        term query 会去倒排索引中寻找确切的term，它并不知道分词器的存在，这种查询适合keyword,numeric,date的查询
//        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("author", keyword);
////        terms 查询某个字段含有多个关键字的文档
//        List<String> keywords = Arrays.asList(keyword.split(","));
//        TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("title",keywords);
////        multi_match可以匹配多个字段
//        MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword,"title","author","content");
////       matchPhrase短语匹配查询，需要完全匹配这个短语
//        MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery(keyword,"title");
////      精确匹配查询的短语，需要全部单词和顺序要完全一样，标点符号除外     match_phrase_prefix 和 match_phrase 用法是一样的，区别就在于它允许对最后一个词条前缀匹配
//        MatchPhrasePrefixQueryBuilder matchPhrasePrefixQueryBuilder = QueryBuilders.matchPhrasePrefixQuery("content", keyword);
////        range范围查询
//        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("birthday").from("1990-10-10").to("2020-01-12").includeLower(true).includeUpper(true);
////        wildcard 查询，是通配符查询，*表示0个或多个，? 表示1个字符
//        WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("title",keyword);
////        fuzzy 实现模糊查询
//        FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("content",keyword);

        String escape = QueryParser.escape(keyword);
        // match 查询：先进行分词，再查询
        MatchQueryBuilder blogContentMatchQueryBuilder = QueryBuilders.matchQuery("blogContent", escape);
        MatchQueryBuilder blogTitleMatchQueryBuilder = QueryBuilders.matchQuery("blogTitle", escape);
        MatchQueryBuilder blogAuthorMatchQueryBuilder = QueryBuilders.matchQuery("blogAuthor", escape);
        boolQueryBuilder.should(blogContentMatchQueryBuilder).should(blogTitleMatchQueryBuilder).should(blogAuthorMatchQueryBuilder);
//
//        //后置过滤
//        sourceBuilder.postFilter();

//        //排除、包含
//        String[] include = "title,author".split(",");
//        String[] exclude = {"content"};
//        sourceBuilder.fetchSource(include,exclude);
        sourceBuilder.query(boolQueryBuilder);

        // 分页
        sourceBuilder.from(pageNo);
        sourceBuilder.size(20);
        //按date降序
        sourceBuilder.sort(SortBuilders.fieldSort("modTime").unmappedType("date").order(SortOrder.DESC));
        // 添加条件
        searchRequest.source(sourceBuilder);

        try {
            SearchResponse search = rhlClient.search(searchRequest, RequestOptions.DEFAULT);
            if(Objects.nonNull(search)&&Objects.nonNull(search.getHits())){
                SearchHits hits = search.getHits();
                for (SearchHit hit : hits.getHits()) {
                    Map<String, Object> blogMap = hit.getSourceAsMap();
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                    if (Objects.isNull(blogMap) || blogMap.size() == 0) {
                        continue;
                    }

                    Blog blog = JSON.parseObject(JSON.toJSONString(blogMap), Blog.class);
                    blog.setBlogId((Long) blogMap.get("blogId"));
                    if(Objects.nonNull(highlightFields.get("blogContent"))){
                        HighlightField contentHignLigntField = highlightFields.get("blogContent");
                        // 获取到原有内容中 每个高亮显示 集中位置 fragment 就是高亮片段
                        Text[] fragments = contentHignLigntField.fragments();
                        String content = "";
                        for (Text text : fragments) {
                            content += text;
                        }
                        blog.setBlogContent(content);
                    }
                    if(Objects.nonNull(highlightFields.get("blogTitle"))){
                        HighlightField titleHignLigntField = highlightFields.get("blogTitle");
                        // 获取到原有内容中 每个高亮显示 集中位置 fragment 就是高亮片段
                        Text[] fragments = titleHignLigntField.fragments();
                        String title = "";
                        for (Text text : fragments) {
                            title += text;
                        }
                        blog.setBlogTitle(title);
                    }
                    blogList.add(blog);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return blogList;
    }
}
